COMP3141
Software System Design and Implementation (18s1)

Exercise (Week 5)

Table of Contents

Ex03-icon.png

DUE: Sun 8 April 23:59:59

1 Getting Started

Download the exercise tarball and extract it to a directory in your home directory at CSE. This tarball contains a file, called Ex03.hs, wherein you will do all of your programming.

To test your code, run the following shell commands to open a GHCi session:

$ 3141
newclass starting new subshell for class COMP3141...
$ cabal repl
Resolving dependencies...
Configuring Ex03-1.0...
Preprocessing executable 'Ex03' for Ex03-1.0..
GHCi, version 8.2.2: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Ex03          (Ex03.hs, interpreted)
Ok, one module loaded.
*Ex03> quickCheck prop_mysteryPred_1
...

Calling quickCheck in the above way will run the given QuickCheck property with 100 random test cases.

Note that you will only need to submit Ex03.hs, so only make changes to that file.

Download the exercise tarball and extract it to a directory on your local machine. This tarball contains a file, called Ex03.hs, wherein you will do all of your programming.

To test your code, run the following shell commands to open a GHCi session:

$ stack repl
Configuring GHCi with the following packages: Ex03
Using main module: 1. Package 'Ex03' component exe:Ex03 ...
GHCi, version 8.2.2: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Ex03          (Ex03.hs, interpreted)
Ok, one module loaded.
*Ex03> quickCheck prop_mysteryPred_1
...

Calling quickCheck in the above way will run the given QuickCheck property with 100 random test cases.

Note that you will only need to submit Ex03.hs, so only make changes to that file.

Download the Haskell for Mac project and unzip it somewhere on your local machine. Open the project in Haskell for Mac, and begin coding in Ex03.hs. The quickcheck tests have already been placed into the playground for Ex03.hs, so that Haskell for Mac will automatically rerun tests every time you update your code.

Note that you will only need to submit Ex03.hs, so only make changes to that file.

2 QuickCheck and Search Trees

The file Ex03.hs includes the support code described in the following as well as stubs for the three functions that you must implement. The QuickCheck properties discussed below are also included.

We include a binary tree implementation, plus a predicate, isBST, which returns True if a tree is a binary search tree (that is, an infix traversal of the tree is sorted).

data BinaryTree = Branch Integer BinaryTree BinaryTree
                | Leaf
                deriving (Show, Ord, Eq)

isBST :: BinaryTree -> Bool
isBST Leaf = True
isBST (Branch v l r) = and [ allTree (< v) l, allTree (>= v) r
                           , isBST l, isBST r ]
  where allTree :: (Integer -> Bool) -> BinaryTree -> Bool
        allTree f (Branch v l r) = f v && allTree f l && allTree f r
        allTree f (Leaf) = True       

We also include insert and deleteAll functions for binary search trees:

-- Add an integer to a BinaryTree, preserving BST property.
insert :: Integer -> BinaryTree -> BinaryTree
-- Remove all instances of an integer in a binary tree,
-- preserving BST property
deleteAll :: Integer -> BinaryTree -> BinaryTree

An arbitrary generator that generates binary search trees:

searchTrees :: Gen BinaryTree

If we wanted to check our generator, we can check it by running the additional property:

prop_searchTrees = forAll searchTrees isBST

We use the arbitrary search tree generator rather than prefix our properties with a guard like isBST tree ==> to prevent QuickCheck generating lots of spurious test cases.

2.1 Implementing A Mystery Function (Part 1a, 2 Mark)

Write a predicate function mysteryPred, which has the following type signature:

mysteryPred :: Integer -> BinaryTree -> Bool

It must satisfy the following QuickCheck properties:

prop_mysteryPred_1 integer 
  = forAll searchTrees $ \tree -> 
      mysteryPred integer (insert integer tree)

prop_mysteryPred_2 integer
  = forAll searchTrees $ \tree ->
      not (mysteryPred integer (deleteAll integer tree))

You can test your mysteryPred implementation by simply running quickCheck prop_mysteryPred_[1-2] in the playground or GHCi.

2.2 Even more mysterious (Part 1b, 3 Marks)

Write a function mysterious, with the following type signature:

mysterious :: BinaryTree -> [Integer]

It must satisfy the following QuickCheck properties:

prop_mysterious_1 integer 
  = forAll searchTrees $ \tree ->
      mysteryPred integer tree == (integer `elem` mysterious tree)

prop_mysterious_2 = forAll searchTrees $ isSorted . mysterious

isSorted :: [Integer] -> Bool
isSorted (x:y:rest) = x <= y && isSorted (y:rest)
isSorted _ = True

You can test your mysterious implementation by simply running quickCheck prop_mysterious_[1-2] in a playground or GHCi.

2.3 Balancing Act (Part 2, 4 Marks)

First, we have the generator:

sortedListsWithoutDuplicates :: Gen [Integer]

We use a generator to produce sortedListsWithoutDuplicates because guarding our properties with isSorted list ==> etc. means that QuickCheck will waste a lot of time randomly generating lists and checking if they satisfy the guard. We can check if our generator works with the following properties:

prop_sortedListsWithoutDuplicates_1 
  = forAll sortedListsWithoutDuplicates isSorted 
prop_sortedListsWithoutDuplicates_2 
  = forAll sortedListsWithoutDuplicates $ \x -> x == nub x

Write a function astonishing, of type [Integer] -> BinaryTree that satisfies these properties:

prop_astonishing_1 
  = forAll sortedListsWithoutDuplicates $ isBST . astonishing

prop_astonishing_2 
  = forAll sortedListsWithoutDuplicates $ isBalanced . astonishing

prop_astonishing_3 
  = forAll sortedListsWithoutDuplicates $ \ integers -> 
      mysterious (astonishing integers) == integers

Where isBalanced is a predicate that returns true if a tree is height-balanced:

isBalanced :: BinaryTree -> Bool
isBalanced Leaf = True
isBalanced (Branch v l r) = and [ abs (height l - height r) <= 1 
                                , isBalanced l 
                                , isBalanced r
                                ]
  where height Leaf = 0
        height (Branch v l r) = 1 + max (height l) (height r)

Note: This definition of isBalanced is a rather generous definition of balanced trees. Can you find an example of a tree that is not very balanced for which this function would still return True? What would a stricter predicate look like?

3 Submission instructions

You can submit your exercise by typing:

$ give cs3141 Ex03 Ex03.hs

on a CSE terminal, or by using the give web interface. Your file must be named Ex03.hs (case-sensitive!). A dry-run test will partially autotest your solution at submission time. To get full marks, you will need to perform further testing yourself.

2018-06-14 Thu 18:28

Announcements RSS